package com.itheima.news163;

import com.google.gson.Gson;
import com.itheima.dao.NewsDao;
import com.itheima.pojo.News;
import com.itheima.pojo.SpiderFinal;
import com.itheima.tools.HttpClientUtils;
import com.itheima.tools.IdWorker;
import com.itheima.tools.JedisUtil;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import redis.clients.jedis.Jedis;

import java.io.IOException;
import java.util.List;
import java.util.Map;

/**
 * 163新闻爬取功能类
 */
public class News163_Spider {
    /*
    确定首页地址为: http://ent.163.com/special/000380VU/newsdata_index.js?callback=data_callback
    通过下拉确定了下一页 http://ent.163.com/special/000380VU/newsdata_index_02.js?callback=data_callback
    */
    /**
     * 分布式id生成器
     */
    private static IdWorker idWorker = new IdWorker(0,0);
    private static NewsDao newsDao = new NewsDao();

    public static void main(String[] args) throws IOException {
        //确定获取新闻的url
        String url = "http://ent.163.com/special/000380VU/newsdata_index.js?callback=data_callback";
        //调用工具,获取页面的json数据,工具的作用是给定一个url,返回给你一个html页面
        //和之前我封装的方法功能相同,区别是采取了连接池的方式,效率更高,资源消耗更少
        //String jsonNews = HttpClientUtils.doGet(url);
        //解析新闻数据(这里新建一个方法,作用是传入一个类似json的页面数据,帮我解析获取想要的信息)
        //parseJsonNews(jsonNews);

        page163(url);



    }

    /**
     * 下面这个方法的作用是解析页面
     * @param jsonNews
     */
    private static void parseJsonNews(String jsonNews) throws IOException {
        //1 处理json字符串,变成一个格式良好的json
        jsonNews = getJsonString(jsonNews);
//        System.out.println(jsonNews);

        //2 遍历json
        //java对象和json转换,可以使用jackson或者gson
        Gson gson = new Gson();
        //这个方法可以将json转换为对应的对象,又因为页面数据键值对,所以采用ListMap
        List<Map<String,Object>> newsList = gson.fromJson(jsonNews, List.class);
        for (Map<String, Object> newsString : newsList) {
            //通过键找值,获取地址链接
            String docurl = (String) newsString.get("docurl");
            if (docurl.contains("photoview") || docurl.contains("v2") ){
                //如果地址中包含图片photoview 信息,则掠过
                continue;
            }
            //现在得到了每一条的url,先进行判断redis是否包含
            boolean haveUrl = isHaveUrl(docurl);
            if (haveUrl){
                continue;
            }
            //把每一条地址链接都再次进行获取具体的内容,写入数据库
            News news = parseItemNews(docurl);
            //保存数据
//            NewsDao newsDao = new NewsDao();
            newsDao.saveNews(news);

            //走到这里是没有重复,且已经保存到数据库
            //把这个数据保存到redis数据库中,作为查重条件
            saveToRedis(docurl);
        }
    }


    /**
     * 这里的作用是处理json字符串,使其格式良好
     * @param jsonNews
     * @return
     */
    private static String getJsonString(String jsonNews) {
        //获取标准json格式外层的符号
        int start = jsonNews.indexOf("(");
        int end = jsonNews.indexOf(")");
        //通过截取的方式保留格式良好的json字符串返回(注意含头不含尾)
        jsonNews = jsonNews.substring(start+1, end);
        return jsonNews;
    }


    /**
     * 解析其中一条新闻链接
     * @param docurl
     */
    private static News parseItemNews(String docurl) throws IOException {
        News news = new News();
        //获取到了地址链接,开始访问请求,获取每一条新闻的html,利用工具类
        String newsHtml = HttpClientUtils.doGet(docurl);
        //转换document对象
        Document document = Jsoup.parse(newsHtml);
        //获取页面标题
        String title = document.select("#epContentLeft h1").text();
        System.out.println(title);
        //获取新闻的时间和来源
        String timeAndSource = document.select(".post_time_source").text();
        String[] strings = timeAndSource.split("　来源: ");
        System.out.println(strings[0] + strings[1]);
        String time = strings[0];
        String source = strings[1];
        //内容
        String content = document.select("#endText p").text();
//        System.out.println(content);

        //编辑
        String text = document.select(".ep-editor").text();
        String[] editors = text.split("：");
        String editor = editors[1];
//        System.out.println(editor);

        //将获取到的数据设置到对象中去
        //id需要注意,要考虑到多个服务器或多个数据库的id唯一性

        news.setId(new IdWorker().nextId() + "");

        news.setDocurl(docurl);
        news.setEditor(editor);
        news.setContent(content);
        news.setTime(time);
        news.setTitle(title);
        news.setSource(source);

        return news;

    }

    /**
     * 分页处理
     */

    public static void page163(String indexUrl) throws IOException {
        //接替首页的操作,转为分页直接操作
        String url = indexUrl;
        int page = 2;
        //先爬取第一页
        while (true){
            //先爬取第一页数据
            String jsonNews = HttpClientUtils.doGet(url);
            if(StringUtils.isEmpty(jsonNews)){
                //没有数据跳出循环
                break;
            }
            parseJsonNews(jsonNews);

            //构造下一页的url
            String pageString = "";
            if(page < 10){
                pageString = "0" + page;
            }else{
                pageString = page + "";
            }
            page++;
            url = "http://ent.163.com/special/000380VU/newsdata_index_"+ pageString +".js";
            //循环爬取下一页
        }
    }

    /**
     * 去重处理
     */
    private static boolean isHaveUrl(String url){
        //核心思想是存入redis,然后进行比较,redis内存数据库,读取远超mysql
        //每天定时爬取,任务量大

        //获取redis链接
        Jedis jedis = JedisUtil.getJedis();
        //判断是否存在
        Boolean sismember = jedis.sismember(SpiderFinal.SPIEDR_NEWS163, url);
        jedis.close();
        return sismember;
    }

    /**
     * 保存到redis的方法
     */
    private static void saveToRedis(String docurl){
        Jedis jedis = JedisUtil.getJedis();

        //把这个url存到redis中,key的值就是定义的常量名称
        jedis.sadd(SpiderFinal.SPIEDR_NEWS163,docurl);

        jedis.close();
    }



}
